package com.szwistar.common.comm;

import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fasterxml.jackson.annotation.JsonIgnore;


//
// 关于重发和异步结果处理
//
public class AsyncCmdAck extends AsyncResult<Object> {
	static final Logger LOG = LoggerFactory.getLogger(AsyncCmdAck.class);
	static final Logger LOGCOMM = LoggerFactory.getLogger("comm");

	public static final AsyncCmdAck ok() {
		return new AsyncCmdAck(null, 0, null, null);
	}

	public static final AsyncCmdAck ok(Object ret) {
		return new AsyncCmdAck(null, 0, null, ret);
	}

	public static final AsyncCmdAck error(int code, String msg) {
		return new AsyncCmdAck(null, code, msg, null);
	}

	// 命令
	AsyncCommand cmd = null;

	public AsyncCommand getCmd() { return cmd; }

	AsyncCmdAck(AsyncCommand cmd, int errCode, String msg, Object result) {
		super(errCode, msg, result);
		this.cmd = cmd;
	}

	/**
	 * 创建针对一个发送消息的异步返回结果
	 * @param cmd 已经发送的消息
	 */
	public AsyncCmdAck(AsyncCommand cmd) {
		super(0);
		this.cmd = cmd;
	}

	@Override
	public String toString() {
		return String.format("ShipAsyncCmdAck[errCode=%d, msg='%s', result='%s', cmd='%s']",
				errCode, msg, result==null?null:result.toString(), cmd==null?null:cmd.toString());
	}

	@JsonIgnore	@Override
	public Object get() throws InterruptedException { return get(waitTimeout); }
	@JsonIgnore @Override
	public Object get(long timeout) throws InterruptedException { return get(timeout, TimeUnit.MILLISECONDS); }
	@JsonIgnore @Override
	public Object get(long timeout, TimeUnit unit) throws InterruptedException { return waitReturn(timeout, unit) ? result : null; }
	@Override
	public boolean waitReturn() { return (cmd == null) ? super.waitReturn() : waitReturn(cmd.getResendCount()*cmd.getResendInterval()); }
	@Override
	public boolean waitReturn(long timeout) { return waitReturn(timeout, TimeUnit.MILLISECONDS); }

	/**
	 * 重载此函数，以便在需要等待返回时，加入重发机制
	 */
	@Override
	protected boolean waitReturn(long timeout, TimeUnit unit) {
		//System.out.println("cmds??");
		if(cmd == null) { return super.waitReturn(timeout, unit); }
		if(!isPending()) { return true; }
		if(timeout <= 0) { return false; }

		// 如果需要等待，则把此命令添加到要等待的队列中
		synchronized(cmds) { cmds.add(this); }
		//System.out.println("cmds++");

		// 如果重发线程未启动，则启动它
		tryStartResendTask();

		return super.waitReturn(timeout, unit);
	}

	//
	// 关于超时重发和异步等待处理
	//
	/**
	 * 在未返回的CMD列表中查找ACK帧对应的CMD
	 */
	public static AsyncCmdAck matchResponse(Object ack) {
		synchronized(cmds) {
			//System.out.println("cmds??");
			for(AsyncCmdAck cmdAck: cmds) {
				if(cmdAck.cmd != null && cmdAck.cmd.matchResponse(ack)) {
					cmdAck.doneWithResult(0, null, ack);
					//System.out.println("cmds==");
					return cmdAck;
				}
			}
		}
		return null;
	}

	/**
	 * 检查是否需要再次发送，如果需要再次发送，则立即发送，并返回 true；否则返回false;
	 */
	boolean testToResend() {
		// 以下情况下，不用再等或再重发了：
		// 1) 结果已经完成：!cmdAck.isPending()
		if(!isPending()) { return false; }
		// 2) 重发次数已经到达
		if(cmd.getResentCount() >= cmd.getResendCount()) { return false; }
		// 3) 连接已断开
		if(cmd.getSender() == null || !cmd.getSender().isReady()) { return false; }

		if(getElapsedTime() >= cmd.getResentCount()*cmd.getResendInterval()) {
			//System.out.printf("cmd: %d >= %d\r\n", getElapsedTime(), cmd.getResentCount()*cmd.getResendInterval());
			cmd.getSender().doSend(cmd, true);
		}
		return true;
	}

	// FIXME 当有海量的设备时，使用单线程进行指令发送是否会影响性能？

	// FIXME 对于同一个设备，是否应该增加流控功能？可考虑为每个连接单独建立一个指令队列！

	// 需要超时重发处理的指令列表(必须要线程安全的)
	static ConcurrentLinkedQueue<AsyncCmdAck> cmds = new ConcurrentLinkedQueue<AsyncCmdAck>();
	// 超时重发线程
	static Thread resendTask = null;
	// 启动超时重发线程
	static void tryStartResendTask() {
		// 如果已经启动，则直接返回
		if(resendTask != null) { return; }

		resendTask = new Thread(new Runnable() {
			@Override
			public void run() {
				Thread thisThread = Thread.currentThread();

				// 循环直到指令列表为空
				while(thisThread == resendTask && !cmds.isEmpty()) {
					synchronized(cmds) {
						for(AsyncCmdAck cmdAck: cmds) {
							if(!cmdAck.testToResend()) {
								// 已经结束，删除它
								//System.out.println("cmds--");
								cmds.remove(cmdAck);
								continue;
							}
						}
					}

					// 稍微休眠一下，以减少CPU消耗
					try { Thread.sleep(1); } catch (Exception e) { break; }
				}

				// 线程结束时要清除线程句柄
				resendTask = null;
			}
		}, "超时重发线程");

		resendTask.start();
	}
}
